package com.stars.easyms.feign.request;

import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.trace.EasyMsTraceSynchronizationManager;
import com.stars.easyms.base.util.*;
import com.stars.easyms.feign.plugin.EasyMsFeignPluginRegister;
import com.stars.easyms.feign.properties.EasyMsFeignProperties;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Map;

import static feign.form.ContentProcessor.CONTENT_TYPE_HEADER;

/**
 * <p>className: BaseRequestInterceptor</p>
 * <p>description: 基础feign调用请求拦截器</p>
 *
 * @author guoguifang
 * @version 1.6.3
 * @date 2020/9/17 2:34 下午
 */
class FeignRequestInterceptor {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final EasyMsFeignProperties easyMsFeignProperties;

    FeignRequestInterceptor() {
        this.easyMsFeignProperties = ApplicationContextHolder.getApplicationContext().getBean(EasyMsFeignProperties.class);
    }

    void traceRequest(RequestTemplate requestTemplate, Object object, String contentType) {
        // 如果已经设置了traceId则不再重复设置
        if (requestTemplate.headers().get(HttpHeaderConstants.TRACE_KEY) != null) {
            return;
        }

        // 获取请求头的ContentType值
        if (contentType == null) {
            contentType = getContentTypeValue(requestTemplate.headers());
        }

        // 获取全局追踪ID，用于微服务日志全链路跟踪，如果为空则创建一个
        String traceId = TraceUtil.getTraceId(EasyMsTraceSynchronizationManager.getTraceId());
        String applicationName = SpringBootUtil.getApplicationName();
        String requestId = TraceUtil.getTraceId();
        String requestTime = DateTimeUtil.now();
        String asyncId = EasyMsTraceSynchronizationManager.getAsyncId();

        // 获取响应系统及请求路径
        String responseSys = requestTemplate.feignTarget().name();
        responseSys = responseSys.startsWith("http://") ? responseSys.substring(7) : responseSys;
        responseSys = responseSys.startsWith("https://") ? responseSys.substring(8) : responseSys;
        String requestUrl = requestTemplate.url();
        int responseSysIndex = requestUrl.indexOf(responseSys);
        String requestPath = responseSysIndex < 0 ? requestUrl : requestUrl.substring(responseSysIndex + responseSys.length());

        // 将参数放入请求头中
        requestTemplate.header(HttpHeaderConstants.TRACE_KEY, traceId);
        requestTemplate.header(HttpHeaderConstants.HEADER_KEY_REQUEST_PATH, requestPath);
        requestTemplate.header(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS, applicationName);
        requestTemplate.header(HttpHeaderConstants.HEADER_KEY_RESPONSE_SYS, responseSys);
        requestTemplate.header(HttpHeaderConstants.HEADER_KEY_REQUEST_ID, requestId);
        requestTemplate.header(HttpHeaderConstants.HEADER_KEY_REQUEST_TIME, requestTime);
        if (asyncId != null) {
            requestTemplate.header(HttpHeaderConstants.HEADER_KEY_ASYNC_ID, asyncId);
        }

        // 将用户信息放入请求头中
        String userInfo = EasyMsTraceSynchronizationManager.getCurrentUserInfo();
        if (userInfo != null) {
            requestTemplate.header(HttpHeaderConstants.HEADER_KEY_USER_INFO, userInfo);
        }

        // 记录请求日志
        if (!EasyMsFeignPluginRegister.isIgnoredTraceUrl(requestPath)) {
            String httpMethodName = requestTemplate.method();
            logger.info("[调用服务-请求]-[请求地址: {}]-[请求系统: {}]-[服务系统: {}]-[请求ID: {}]{}-[请求时间: {}]" +
                            "-[请求Content-Type: {}]-[请求Method: {}]{}.",
                    requestPath, applicationName, responseSys, requestId, TraceUtil.getAsyncIdTrace(asyncId),
                    requestTime, contentType, httpMethodName,
                    easyMsFeignProperties.isLogRequest() && object != null ? "-请求数据: " + JsonUtil.toJSONString(object) : "");
        }
    }

    String getContentTypeValue(Map<String, Collection<String>> headers) {
        for (Map.Entry<String, Collection<String>> entry : headers.entrySet()) {
            if (CONTENT_TYPE_HEADER.equalsIgnoreCase(entry.getKey())) {
                for (String contentTypeValue : entry.getValue()) {
                    if (contentTypeValue != null) {
                        return contentTypeValue;
                    }
                }
            }
        }
        return null;
    }

}
